﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Diagnostics;
using System.Xml;
using System.Globalization;

namespace GPCodeConjurer
{
	public class Arc : Node, IRectangularNode
	{
		public Arc()
		{
			this.Rectangle = new RectangleF(0, 0, 50, 50);
			this.startAngle = 45f;
			this.sweepAngle = 180f;
			var rect = this.Rect;

			// Add grabhandles
			this.AddRectangularGrabHandles();

			// And add some glyph handles.
			PointF startPoint = PointF.Empty;
			PointF endPoint = PointF.Empty;
			using (var gp = new GraphicsPath())
			{
				this.DrawPath(gp);
				startPoint = gp.PathPoints[0];
				endPoint = gp.PathPoints[gp.PointCount - 1];
			}
			this.GrabHandles.Add(
				":START",
				new GrabHandle(
					this,
					HandleMoveBehavior.NodeCircular,
					ContentAlignment.MiddleCenter,
					HandleShape.Diamond,
					Color.Yellow)
				);
			this.GrabHandles.Add(
				":END",
				new GrabHandle(
					this,
					HandleMoveBehavior.NodeCircular,
					ContentAlignment.MiddleCenter,
					HandleShape.Diamond,
					Color.Green)
				);

		}

		public Arc(RectangleF rectangle, float startAngle, float sweepAngle)
			: this()
		{
			this.Rectangle = rectangle;
			this.startAngle = startAngle;
			this.sweepAngle = sweepAngle;
			this.RelocateGrabHandles();
		}


		public RectangleF Rectangle { get; set; }



		private float startAngle = 0f;
		public float StartAngle
		{
			get
			{
				return this.startAngle;
			}
			set
			{
				if (this.startAngle != value)
				{
					this.startAngle = Math.Max(0f, Math.Min(360f, value));
					this.RecalcGlyphPositions();
					this.NotifyPropertyChanged("StartAngle");
					this.Invalidate();
				}
			}
		}


		private float sweepAngle = 0f;
		public float SweepAngle 
		{ 
			get
			{
				return this.sweepAngle;
			}
			set
			{
				if (this.sweepAngle != value)
				{
					this.sweepAngle = Math.Max(-360f, Math.Min(360f, value));
					this.RecalcGlyphPositions();
					this.NotifyPropertyChanged("SweepAngle");
					this.Invalidate();
				}
			}
		}



		internal override Rectangle Rect
		{ 
			get
			{
				return new Rectangle(
					Convert.ToInt32(this.Rectangle.X),
					Convert.ToInt32(this.Rectangle.Y),
					Convert.ToInt32(this.Rectangle.Width),
					Convert.ToInt32(this.Rectangle.Height)
					);
			}
		}



		internal override void DrawPath(GraphicsPath gp)
		{
			var rect = this.Rectangle.Absolute();
			if (rect.Width > 0 && rect.Height > 0)
				gp.AddArc(rect, this.StartAngle, this.SweepAngle);
		}

		public override string CreateCode()
		{
			string c = string.Empty;
			if (this.StartFigure)
				c += Environment.NewLine + "\tgp.StartFigure();";
			c += string.Format(
				"\r\n\tgp.AddArc(new RectangleF({0}f, {1}f, {2}f, {3}f), {4}f, {5}f);",
				this.Rectangle.X,
				this.Rectangle.Y,
				this.Rectangle.Width,
				this.Rectangle.Height,
				this.StartAngle,
				this.SweepAngle);
			if (this.CloseFigure)
				c += Environment.NewLine + "\tgp.CloseFigure();";
			return c;
		}

		internal override bool PointIsOnNode(Point pt)
		{
			using (GraphicsPath gp = new GraphicsPath())
			{
				Pen pen = new Pen(Color.Black, 10f);
				this.DrawPath(gp);
				gp.Widen(pen);
				pen.Dispose();
				return gp.IsVisible(pt);
			}
		}

		internal override void OnGrabHandleMove(HandleMovedEventArgs e)
		{
			var grabHandle = e.GrabHandle;
			if (grabHandle.Key.StartsWith(":") == false)
			{

				var rect = this.Rect;
				var newRect = this.Rectangle;

				if (grabHandle.Key.Contains("N"))
				{
					newRect.Height = rect.Bottom - grabHandle.Location.Y;
					newRect.Y = grabHandle.Location.Y;
				}
				if (grabHandle.Key.Contains("S"))
				{
					newRect.Height = grabHandle.Location.Y - rect.Y;
				}
				if (grabHandle.Key.Contains("E"))
				{
					newRect.Width = grabHandle.Location.X - rect.X;
				}
				if (grabHandle.Key.Contains("W"))
				{
					newRect.Width = rect.Right - grabHandle.Location.X;
					newRect.X = grabHandle.Location.X;
				}


				// Check of inverted size and change movebehavior if needed.
				bool inverted = ((this.Rectangle.Width < 0 && this.Rectangle.Height > 0) || (this.Rectangle.Height < 0 && this.Rectangle.Width > 0));
				if (inverted)
				{
					this.GrabHandles["NE"].MoveBehavior = HandleMoveBehavior.NWSE;
					this.GrabHandles["NW"].MoveBehavior = HandleMoveBehavior.NESW;
					this.GrabHandles["SE"].MoveBehavior = HandleMoveBehavior.NESW;
					this.GrabHandles["SW"].MoveBehavior = HandleMoveBehavior.NWSE;
				}
				else
				{
					this.GrabHandles["NE"].MoveBehavior = HandleMoveBehavior.NESW;
					this.GrabHandles["NW"].MoveBehavior = HandleMoveBehavior.NWSE;
					this.GrabHandles["SE"].MoveBehavior = HandleMoveBehavior.NWSE;
					this.GrabHandles["SW"].MoveBehavior = HandleMoveBehavior.NESW;
				}

				this.Rectangle = newRect.Absolute();
				this.NotifyPropertyChanged("Rectangle");

				// change loc of handles
				this.RelocateGrabHandles();
			}
			else
			{
				// ok, moved a glyph -- recalc the start- and sweepangle.
				if (grabHandle.Key == ":START")
				{
					this.StartAngle = e.GDIAngle;
					this.NotifyPropertyChanged("StartAngle");
				}
				else if (grabHandle.Key == ":END")
				{
					bool positive = (this.sweepAngle >= 0);
					float diffAngle = e.GDIAngle - this.startAngle;
					if (positive && diffAngle < 0f)
						diffAngle += 360f;
					if (!positive && diffAngle > 0f)
						diffAngle -= 360;

					// Check for cross-over.
					if (Math.Abs(diffAngle - this.sweepAngle) > 180f)
					{
						if (positive)
							diffAngle -= 360f;
						else
							diffAngle += 360f;
						Debug.WriteLine(diffAngle);
						//diffAngle = 360 + diffAngle;
					}
					this.sweepAngle = diffAngle;
					this.NotifyPropertyChanged("SweepAngle");
				}
			}
		}


		internal void RecalcGlyphPositions()
		{
			// Change location of the glyph handles
			// And add some handles.
			PointF startPoint = PointF.Empty;
			PointF endPoint = PointF.Empty;
			using (var gp = new GraphicsPath())
			{
				this.DrawPath(gp);
				if (gp.PointCount > 0)
				{
					startPoint = gp.PathPoints[0];
					endPoint = gp.PathPoints[gp.PointCount - 1];
				}
			}
			this.GrabHandles[":START"].Location = new PointF(startPoint.X, startPoint.Y).ToPoint();
			this.GrabHandles[":END"].Location = new PointF(endPoint.X, endPoint.Y).ToPoint();

		}


		internal override void OnMoved(NodeMovedEventArgs e)
		{
			this.Rectangle = new RectangleF(
				this.Rectangle.Location.X + e.Difference.Width,
				this.Rectangle.Location.Y + e.Difference.Height,
				this.Rectangle.Width,
				this.Rectangle.Height);

			// move the handles also
			foreach (var kvp in this.GrabHandles)
			{
				kvp.Value.Location = new Point(kvp.Value.Location.X + e.Difference.Width, kvp.Value.Location.Y + e.Difference.Height);
			}

			this.NotifyPropertyChanged("Rectangle");

		}

		internal override void RelocateGrabHandles()
		{
			var newRect = this.Rectangle;
			this.GrabHandles["NW"].Location = newRect.Location.ToPoint();
			this.GrabHandles["N"].Location = new PointF(newRect.Left + newRect.Width / 2, newRect.Top).ToPoint();
			this.GrabHandles["NE"].Location = new PointF(newRect.Right, newRect.Top).ToPoint();
			this.GrabHandles["W"].Location = new PointF(newRect.Left, newRect.Top + newRect.Height / 2).ToPoint();
			this.GrabHandles["E"].Location = new PointF(newRect.Right, newRect.Top + newRect.Height / 2).ToPoint();
			this.GrabHandles["SW"].Location = new PointF(newRect.Left, newRect.Bottom).ToPoint();
			this.GrabHandles["S"].Location = new PointF(newRect.Left + newRect.Width / 2, newRect.Bottom).ToPoint();
			this.GrabHandles["SE"].Location = new PointF(newRect.Right, newRect.Bottom).ToPoint();
			this.RecalcGlyphPositions();
		}


		public override object Clone()
		{
			Arc n = new Arc(this.Rectangle, this.startAngle, this.sweepAngle);
			return n;
		}

		public override XmlElement Serialize(XmlDocument xmlDoc)
		{
			var n = xmlDoc.CreateElement("Arc");
			this.SeralizeStartCloseFigure(n);
			n.SetAttribute("startAngle", this.startAngle.ToString(CultureInfo.InvariantCulture));
			n.SetAttribute("sweepAngle", this.sweepAngle.ToString(CultureInfo.InvariantCulture));
			n.AppendChild(this.Rectangle.Serialize(xmlDoc));
			return n;
		}



		public override void Deserialize(XmlElement seralizedElement)
		{
			this.DeseralizeStartCloseFigure(seralizedElement);
			this.startAngle = float.Parse(seralizedElement.GetAttribute("startAngle"), CultureInfo.InvariantCulture);
			this.sweepAngle = float.Parse(seralizedElement.GetAttribute("sweepAngle"), CultureInfo.InvariantCulture);
			this.Rectangle = ExtensionHelpers.DeserializeRectangleF(seralizedElement.SelectSingleNode("RectangleF") as XmlElement);
		}




		public override string ToString()
		{
			return "Arc";
		}

	}
}
